"""
期权定价模块
获取期权详细定价信息
"""
import time
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from loguru import logger

try:
    import okx.PublicData as PublicData
except ImportError:
    logger.warning("okx SDK未安装，请运行: pip install okx")
    PublicData = None


class OptionSummaryManager:
    """期权定价管理器"""
    
    # 限速配置：20次/2s
    RATE_LIMIT_REQUESTS = 20
    RATE_LIMIT_WINDOW = 2.0  # 秒
    
    def __init__(self, sandbox: bool = True):
        """
        初始化期权定价管理器
        
        Args:
            sandbox: 是否使用模拟盘，True=模拟盘(1)，False=实盘(0)
        """
        if PublicData is None:
            raise ImportError("okx SDK未安装，请运行: pip install okx")
        
        self.sandbox = sandbox
        flag = "1" if sandbox else "0"
        
        # 初始化OKX公共数据API
        self.public_api = PublicData.PublicAPI(flag=flag)
        
        # 限速控制 - 按instFamily分别限速
        self.request_times: Dict[str, List[float]] = {}  # inst_family -> 请求时间戳列表
        
        # 数据缓存
        self.option_summary_cache: Dict[str, List[Dict]] = {}  # cache_key -> option_summary_data
        self.cache_expire_time: Dict[str, datetime] = {}  # cache_key -> expire_time
        self.cache_duration = timedelta(minutes=5)  # 缓存5分钟
        
        logger.info(f"期权定价管理器已初始化 (模式: {'模拟盘' if sandbox else '实盘'})")
    
    def _check_rate_limit(self, inst_family: str):
        """
        检查并执行限速
        限速规则：20次/2s (IP + instFamily)
        
        Args:
            inst_family: 交易品种，如 BTC-USD
        """
        now = time.time()
        
        # 初始化该品种的请求记录
        if inst_family not in self.request_times:
            self.request_times[inst_family] = []
        
        # 移除2秒前的请求记录
        self.request_times[inst_family] = [t for t in self.request_times[inst_family]
                                           if now - t < self.RATE_LIMIT_WINDOW]
        
        # 如果达到限速上限，等待
        if len(self.request_times[inst_family]) >= self.RATE_LIMIT_REQUESTS:
            oldest_request = self.request_times[inst_family][0]
            wait_time = self.RATE_LIMIT_WINDOW - (now - oldest_request)
            
            if wait_time > 0:
                logger.debug(f"触发限速 ({inst_family})，等待 {wait_time:.2f} 秒")
                time.sleep(wait_time)
                # 重新计算
                now = time.time()
                self.request_times[inst_family] = [t for t in self.request_times[inst_family]
                                                   if now - t < self.RATE_LIMIT_WINDOW]
        
        # 记录本次请求
        self.request_times[inst_family].append(now)
    
    def get_option_summary(self, uly: str, 
                          exp_time: Optional[str] = None,
                          use_cache: bool = True) -> List[Dict]:
        """
        获取期权定价
        
        Args:
            uly: 标的指数，如 BTC-USD
            exp_time: 到期日，格式为 YYMMDD，如 250628
            use_cache: 是否使用缓存
            
        Returns:
            期权定价信息列表，每个期权包含以下字段：
            - instType: 产品类型 (OPTION)
            - instId: 产品ID
            - uly: 标的指数
            - delta: Delta值
            - gamma: Gamma值
            - vega: Vega值
            - theta: Theta值
            - deltaBS: BS模型Delta
            - gammaBS: BS模型Gamma
            - vegaBS: BS模型Vega
            - thetaBS: BS模型Theta
            - lever: 杠杆倍数
            - markVol: 标记波动率
            - bidVol: 买方波动率
            - askVol: 卖方波动率
            - realVol: 已实现波动率
            - volLv: 波动率等级
            - fwdPx: 远期价格
            - ts: 数据返回时间（毫秒时间戳）
        """
        # 生成缓存键
        cache_key = f"{uly}_{exp_time}"
        
        # 检查缓存
        if use_cache and cache_key in self.option_summary_cache:
            expire_time = self.cache_expire_time.get(cache_key)
            if expire_time and datetime.now() < expire_time:
                logger.debug(f"从缓存获取期权定价: {cache_key}")
                return self.option_summary_cache[cache_key]
        
        # 执行限速检查
        self._check_rate_limit(uly)
        
        try:
            # 构建请求参数
            params = {"uly": uly}
            if exp_time:
                params["expTime"] = exp_time
            
            logger.info(f"获取期权定价: {params}")
            result = self.public_api.get_opt_summary(**params)
            
            # 检查响应
            if result.get("code") != "0":
                error_msg = result.get("msg", "未知错误")
                logger.error(f"获取期权定价失败: {error_msg}")
                raise RuntimeError(f"API错误: {error_msg}")
            
            # 提取数据
            data_list = result.get("data", [])
            logger.info(f"获取期权定价成功: {len(data_list)} 个期权")
            
            # 更新缓存
            if use_cache:
                self.option_summary_cache[cache_key] = data_list
                self.cache_expire_time[cache_key] = datetime.now() + self.cache_duration
            
            return data_list
            
        except Exception as e:
            logger.error(f"获取期权定价异常: {e}")
            raise
    
    def get_option_by_id(self, inst_id: str, use_cache: bool = True) -> Optional[Dict]:
        """
        根据产品ID获取单个期权的定价信息
        
        Args:
            inst_id: 产品ID，如 BTC-USD-250628-50000-C
            use_cache: 是否使用缓存
            
        Returns:
            期权定价信息字典，如果不存在返回None
        """
        # 从inst_id提取标的指数
        # 格式: BTC-USD-250628-50000-C
        parts = inst_id.split("-")
        if len(parts) < 3:
            raise ValueError(f"无效的期权产品ID: {inst_id}")
        
        uly = f"{parts[0]}-{parts[1]}"
        exp_time = parts[2] if len(parts) > 2 else None
        
        try:
            options = self.get_option_summary(uly, exp_time, use_cache)
            
            # 查找匹配的期权
            for option in options:
                if option.get("instId") == inst_id:
                    return option
            
            logger.warning(f"未找到期权: {inst_id}")
            return None
            
        except Exception as e:
            logger.error(f"获取期权定价失败: {inst_id}, {e}")
            return None
    
    def get_option_details(self, inst_id: str) -> Dict:
        """
        获取期权详细信息（包含格式化数据）
        
        Args:
            inst_id: 产品ID
            
        Returns:
            详细信息字典：
            - inst_id: 产品ID
            - inst_type: 产品类型
            - uly: 标的指数
            - greeks: 希腊值字典 (delta, gamma, vega, theta)
            - greeks_bs: BS模型希腊值字典
            - volatility: 波动率信息
            - lever: 杠杆倍数
            - fwd_price: 远期价格
            - timestamp: 数据时间（格式化）
        """
        option_data = self.get_option_by_id(inst_id)
        
        if not option_data:
            raise ValueError(f"无法获取期权定价: {inst_id}")
        
        # 提取数据
        ts = int(option_data.get("ts", 0))
        timestamp_str = datetime.fromtimestamp(ts / 1000).strftime("%Y-%m-%d %H:%M:%S")
        
        return {
            "inst_id": inst_id,
            "inst_type": option_data.get("instType"),
            "uly": option_data.get("uly"),
            "greeks": {
                "delta": float(option_data.get("delta", 0)),
                "gamma": float(option_data.get("gamma", 0)),
                "vega": float(option_data.get("vega", 0)),
                "theta": float(option_data.get("theta", 0)),
            },
            "greeks_bs": {
                "delta": float(option_data.get("deltaBS", 0)),
                "gamma": float(option_data.get("gammaBS", 0)),
                "vega": float(option_data.get("vegaBS", 0)),
                "theta": float(option_data.get("thetaBS", 0)),
            },
            "volatility": {
                "mark_vol": float(option_data.get("markVol", 0)),
                "bid_vol": float(option_data.get("bidVol", 0)),
                "ask_vol": float(option_data.get("askVol", 0)),
                "real_vol": float(option_data.get("realVol", 0)),
                "vol_level": option_data.get("volLv", ""),
            },
            "lever": float(option_data.get("lever", 0)),
            "fwd_price": float(option_data.get("fwdPx", 0)),
            "timestamp": timestamp_str,
            "timestamp_ms": ts,
        }
    
    def get_options_by_expiry(self, uly: str, exp_time: str) -> List[Dict]:
        """
        获取指定到期日的所有期权
        
        Args:
            uly: 标的指数，如 BTC-USD
            exp_time: 到期日，格式为 YYMMDD
            
        Returns:
            期权列表
        """
        return self.get_option_summary(uly, exp_time)
    
    def get_call_options(self, uly: str, exp_time: Optional[str] = None) -> List[Dict]:
        """
        获取看涨期权
        
        Args:
            uly: 标的指数
            exp_time: 到期日（可选）
            
        Returns:
            看涨期权列表
        """
        options = self.get_option_summary(uly, exp_time)
        # 过滤看涨期权（instId以-C结尾）
        call_options = [opt for opt in options if opt.get("instId", "").endswith("-C")]
        return call_options
    
    def get_put_options(self, uly: str, exp_time: Optional[str] = None) -> List[Dict]:
        """
        获取看跌期权
        
        Args:
            uly: 标的指数
            exp_time: 到期日（可选）
            
        Returns:
            看跌期权列表
        """
        options = self.get_option_summary(uly, exp_time)
        # 过滤看跌期权（instId以-P结尾）
        put_options = [opt for opt in options if opt.get("instId", "").endswith("-P")]
        return put_options
    
    def compare_options_volatility(self, uly: str, exp_time: Optional[str] = None) -> List[Dict]:
        """
        比较期权波动率
        
        Args:
            uly: 标的指数
            exp_time: 到期日（可选）
            
        Returns:
            按标记波动率排序的期权列表（从高到低）
        """
        options = self.get_option_summary(uly, exp_time)
        
        # 按标记波动率排序
        sorted_options = sorted(
            options,
            key=lambda x: float(x.get("markVol", 0)),
            reverse=True
        )
        
        return sorted_options
    
    def get_atm_options(self, uly: str, exp_time: Optional[str] = None, 
                       tolerance: float = 0.05) -> List[Dict]:
        """
        获取平值期权（Delta接近0.5的看涨期权或-0.5的看跌期权）
        
        Args:
            uly: 标的指数
            exp_time: 到期日（可选）
            tolerance: Delta容差，默认0.05
            
        Returns:
            平值期权列表
        """
        options = self.get_option_summary(uly, exp_time)
        
        atm_options = []
        for opt in options:
            delta = abs(float(opt.get("delta", 0)))
            # 平值期权的Delta绝对值接近0.5
            if abs(delta - 0.5) <= tolerance:
                atm_options.append(opt)
        
        return atm_options
    
    def clear_cache(self, cache_key: Optional[str] = None):
        """
        清除缓存
        
        Args:
            cache_key: 缓存键，如果为None则清除所有缓存
        """
        if cache_key:
            if cache_key in self.option_summary_cache:
                del self.option_summary_cache[cache_key]
            if cache_key in self.cache_expire_time:
                del self.cache_expire_time[cache_key]
            logger.info(f"已清除 {cache_key} 的缓存")
        else:
            self.option_summary_cache.clear()
            self.cache_expire_time.clear()
            logger.info("已清除所有期权定价缓存")
    
    def get_cache_stats(self) -> Dict:
        """
        获取缓存统计信息
        
        Returns:
            缓存统计字典
        """
        total_cached = len(self.option_summary_cache)
        valid_cached = sum(1 for key in self.option_summary_cache.keys()
                          if key in self.cache_expire_time and
                          datetime.now() < self.cache_expire_time[key])
        
        return {
            "total_cached": total_cached,
            "valid_cached": valid_cached,
            "expired_cached": total_cached - valid_cached,
            "cache_duration_minutes": self.cache_duration.total_seconds() / 60,
        }


# 便捷函数
def get_option_summary(uly: str, sandbox: bool = True, **kwargs) -> List[Dict]:
    """
    便捷函数：获取期权定价
    
    Args:
        uly: 标的指数
        sandbox: 是否使用模拟盘
        **kwargs: 其他参数传递给get_option_summary
        
    Returns:
        期权定价信息列表
    """
    manager = OptionSummaryManager(sandbox=sandbox)
    return manager.get_option_summary(uly, **kwargs)


def get_option_details(inst_id: str, sandbox: bool = True) -> Dict:
    """
    便捷函数：获取期权详细信息
    
    Args:
        inst_id: 产品ID
        sandbox: 是否使用模拟盘
        
    Returns:
        详细信息字典
    """
    manager = OptionSummaryManager(sandbox=sandbox)
    return manager.get_option_details(inst_id)


def get_call_options(uly: str, sandbox: bool = True, exp_time: Optional[str] = None) -> List[Dict]:
    """
    便捷函数：获取看涨期权
    
    Args:
        uly: 标的指数
        sandbox: 是否使用模拟盘
        exp_time: 到期日（可选）
        
    Returns:
        看涨期权列表
    """
    manager = OptionSummaryManager(sandbox=sandbox)
    return manager.get_call_options(uly, exp_time)


def get_put_options(uly: str, sandbox: bool = True, exp_time: Optional[str] = None) -> List[Dict]:
    """
    便捷函数：获取看跌期权
    
    Args:
        uly: 标的指数
        sandbox: 是否使用模拟盘
        exp_time: 到期日（可选）
        
    Returns:
        看跌期权列表
    """
    manager = OptionSummaryManager(sandbox=sandbox)
    return manager.get_put_options(uly, exp_time)
